"""
Animation syntax validator.

Validates animation syntax and parameters for correctness.
"""

from typing import Dict, List, Any, Optional
from ...parser.content_types import AnimationConfig
from ...utils.exceptions import MD2PPTError


class AnimationValidator:
    """Validates animation syntax and parameters."""
    
    # Valid parameter ranges and types
    PARAMETER_RULES = {
        'duration': {'type': (int, float), 'min': 0.1, 'max': 10.0},
        'delay': {'type': (int, float), 'min': 0.0, 'max': 5.0},
        'easing': {'type': str, 'values': ['ease', 'ease-in', 'ease-out', 'ease-in-out', 'linear']},
        'direction': {'type': str, 'values': ['left', 'right', 'up', 'down', 'center']},
        'intensity': {'type': (int, float), 'min': 0.1, 'max': 2.0},
        'repeat': {'type': int, 'min': 1, 'max': 10},
        'reverse': {'type': bool}
    }
    
    # Animation-specific parameter requirements
    ANIMATION_REQUIREMENTS = {
        'slideIn': {'required': [], 'optional': ['direction', 'duration', 'delay', 'easing']},
        'slideOut': {'required': [], 'optional': ['direction', 'duration', 'delay', 'easing']},
        'fadeIn': {'required': [], 'optional': ['duration', 'delay', 'easing']},
        'fadeOut': {'required': [], 'optional': ['duration', 'delay', 'easing']},
        'appear': {'required': [], 'optional': ['delay']},
        'zoom': {'required': [], 'optional': ['intensity', 'duration', 'delay', 'easing']},
        'bounce': {'required': [], 'optional': ['intensity', 'duration', 'delay', 'repeat']},
        'flip': {'required': [], 'optional': ['direction', 'duration', 'delay', 'easing']}
    }
    
    def __init__(self, config: Optional[AnimationConfig] = None):
        """Initialize the validator."""
        self.config = config or AnimationConfig()
    
    def validate_animation_type(self, animation_type: str) -> bool:
        """Validate if animation type is supported."""
        if animation_type not in self.config.supported_animations:
            raise ValidationError(f"Unsupported animation type: {animation_type}")
        return True
    
    def validate_parameters(self, animation_type: str, parameters: Dict[str, Any]) -> Dict[str, Any]:
        """
        Validate animation parameters and return cleaned parameters.
        
        Args:
            animation_type: Type of animation
            parameters: Raw parameters dictionary
            
        Returns:
            Cleaned and validated parameters dictionary
            
        Raises:
            ValidationError: If parameters are invalid
        """
        self.validate_animation_type(animation_type)
        
        # Get requirements for this animation type
        requirements = self.ANIMATION_REQUIREMENTS.get(animation_type, {})
        required_params = requirements.get('required', [])
        optional_params = requirements.get('optional', [])
        all_allowed = required_params + optional_params
        
        cleaned_params = {}
        
        # Check required parameters
        for param in required_params:
            if param not in parameters:
                raise ValidationError(f"Missing required parameter '{param}' for animation '{animation_type}'")
        
        # Validate each parameter
        for param_name, param_value in parameters.items():
            if param_name not in all_allowed:
                raise ValidationError(f"Unknown parameter '{param_name}' for animation '{animation_type}'")
            
            # Validate parameter value
            cleaned_value = self._validate_parameter_value(param_name, param_value)
            cleaned_params[param_name] = cleaned_value
        
        return cleaned_params
    
    def _validate_parameter_value(self, param_name: str, value: Any) -> Any:
        """Validate a single parameter value."""
        if param_name not in self.PARAMETER_RULES:
            # Unknown parameter, return as-is (might be custom)
            return value
        
        rules = self.PARAMETER_RULES[param_name]
        
        # Type validation
        expected_type = rules['type']
        if isinstance(expected_type, tuple):
            if not isinstance(value, expected_type):
                raise ValidationError(f"Parameter '{param_name}' must be of type {expected_type}, got {type(value)}")
        else:
            if not isinstance(value, expected_type):
                raise ValidationError(f"Parameter '{param_name}' must be of type {expected_type}, got {type(value)}")
        
        # Range validation for numeric types
        if isinstance(value, (int, float)):
            if 'min' in rules and value < rules['min']:
                raise ValidationError(f"Parameter '{param_name}' must be >= {rules['min']}, got {value}")
            if 'max' in rules and value > rules['max']:
                raise ValidationError(f"Parameter '{param_name}' must be <= {rules['max']}, got {value}")
        
        # Value validation for strings
        if isinstance(value, str) and 'values' in rules:
            if value not in rules['values']:
                raise ValidationError(f"Parameter '{param_name}' must be one of {rules['values']}, got '{value}'")
        
        return value
    
    def validate_animation_sequence(self, animations: List[Dict[str, Any]]) -> bool:
        """
        Validate a sequence of animations for timing conflicts.
        
        Args:
            animations: List of animation dictionaries with timing info
            
        Returns:
            True if sequence is valid
            
        Raises:
            ValidationError: If sequence has issues
        """
        # Sort animations by start time (delay)
        sorted_animations = sorted(animations, key=lambda x: x.get('delay', 0))
        
        # Check for overlapping animations that might conflict
        for i in range(len(sorted_animations) - 1):
            current = sorted_animations[i]
            next_anim = sorted_animations[i + 1]
            
            current_end = current.get('delay', 0) + current.get('duration', 1.0)
            next_start = next_anim.get('delay', 0)
            
            # Warn about very close timing (might cause visual conflicts)
            if 0 < (next_start - current_end) < 0.1:
                # This is just a warning, not an error
                pass
        
        return True
    
    def get_parameter_info(self, animation_type: str) -> Dict[str, Any]:
        """Get information about parameters for a specific animation type."""
        if animation_type not in self.config.supported_animations:
            raise ValidationError(f"Unknown animation type: {animation_type}")
        
        requirements = self.ANIMATION_REQUIREMENTS.get(animation_type, {})
        
        info = {
            'animation_type': animation_type,
            'required_parameters': requirements.get('required', []),
            'optional_parameters': requirements.get('optional', []),
            'parameter_rules': {}
        }
        
        # Add rules for relevant parameters
        all_params = requirements.get('required', []) + requirements.get('optional', [])
        for param in all_params:
            if param in self.PARAMETER_RULES:
                info['parameter_rules'][param] = self.PARAMETER_RULES[param]
        
        return info


class ValidationError(MD2PPTError):
    """Raised when animation validation fails."""
    pass